﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Collections.Specialized.BitVector32;

namespace JESAI.EventBus.Retrys
{
    internal sealed class EventHandleRetry
    {
        public static void Execute(EventExcuteHandle eventExcuteHandle)
        {
            ExecuteAsync(eventExcuteHandle).GetAwaiter().GetResult();
        }

        public static async Task ExecuteAsync(EventExcuteHandle eventExcuteHandle)
        {
            if (eventExcuteHandle == null) throw new ArgumentNullException(nameof(eventExcuteHandle));

            if (eventExcuteHandle.RetryCount <= 0)
            {
                await eventExcuteHandle.Fuc();
                return;
            }

            var totalRetryCount = eventExcuteHandle.RetryCount;
            var currentRetryCount = 0;
            //剩余重试次数
            var numRetries = totalRetryCount;
            while (true)
            {
                try
                {
                    await eventExcuteHandle.Fuc();
                    break;
                }
                catch (Exception ex)
                {
                    currentRetryCount++;
                    //是否最终抛出异常
                    if (--numRetries < 0)
                    {
                        if (eventExcuteHandle.ExceptionCallback != null)
                        {
                            await eventExcuteHandle.ExceptionCallback.Invoke(new Exception("超过重试次数, 仍然失败", ex));
                            throw;
                        }
                        else return;
                    }

                    //只允许某些特定类型的异常重试
                    if (eventExcuteHandle.ExceptionTypes != null && eventExcuteHandle.ExceptionTypes.Length > 0 && !eventExcuteHandle.ExceptionTypes.Any(u => u.IsAssignableFrom(ex.GetType())))
                    {

                        if (eventExcuteHandle.ExceptionCallback != null)
                        {
                            await eventExcuteHandle.ExceptionCallback.Invoke(new Exception($"不允许重试的异常类型:{ex.GetType()}", ex));
                            throw;
                        }
                        else return;
                    }

                    // 重试输出日志
                    eventExcuteHandle.RetryOutPutAction?.Invoke(totalRetryCount, currentRetryCount);

                    // 如果可重试异常数大于 0，则间隔指定时间后继续执行
                    if (eventExcuteHandle.RetryInterval > 0)
                    {
                        await Task.Delay(eventExcuteHandle.RetryInterval);
                    }

                    // 如果重试间隔数组不为空，则按数组中指定的间隔时间执行
                    if (eventExcuteHandle.RetryIntervals != null && eventExcuteHandle.RetryIntervals.Length > 0)
                    {
                        await Task.Delay(eventExcuteHandle.RetryIntervals[currentRetryCount - 1]);
                    }
                }
            }

        }

        internal sealed class EventExcuteHandle
        {
            public EventExcuteHandle(Func<Task> Fuc)
            {
                this.Fuc = Fuc;
            }
            public Func<Task> Fuc { get; set; }

            public int RetryCount { get; set; } = 0;

            public Func<Exception, Task> ExceptionCallback { get; set; }

            public Type[] ExceptionTypes { get; set; }

            public int RetryInterval { get; set; } = 0;

            public Action<int, int> RetryOutPutAction { get; set; }

            public TimeSpan[] RetryIntervals { get; set; }
        }
    }
}
